from abc import ABC, abstractmethod
from dataclasses import dataclass, asdict
from datetime import datetime
from logging import INFO
from typing import Dict, Any

from ctaf_facade.enumerations.event_type_enum import EventTypeEnum
from ctaf_facade.enumerations.exchange_enum import ExchangeEnum
from ctaf_facade.enumerations.trade_related_enum import IntervalEnum, OrderTypeEnum, DirectionEnum, OffsetEnum, \
    OrderStatusEnum, ProductEnum, OptionTypeEnum


@dataclass
class BaseEventData(ABC):
    def get_event_data_in_str(self) -> str:
        attr_dict: Dict[str, str] = asdict(self)
        attr_strings = [f"{k}: {v}" for k, v in attr_dict.items()]
        return ", ".join(attr_strings)

    def to_dict(self) -> Dict[str, Any]:
        """Converts the dataclass instance to a dictionary for serialization."""
        data = asdict(self)
        # Handle datetime objects
        for key, value in data.items():
            if isinstance(value, datetime):
                data[key] = value.isoformat()
            elif isinstance(value, (ExchangeEnum, IntervalEnum, OrderTypeEnum, DirectionEnum, OffsetEnum,
                                    OrderStatusEnum, ProductEnum, OptionTypeEnum)):
                data[key] = value.value
            elif value is None:
                pass
        return data

    @classmethod
    @abstractmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BaseEventData":
        """Abstract method requiring subclasses to implement their own deserialization logic."""
        pass  # 不再尝试在基类中进行实例化


@dataclass
class LogEventData(BaseEventData):
    msg: str
    level: int = INFO
    time: datetime = None  # 新增字段并加入 __init__

    def __post_init__(self) -> None:
        if self.time is None:
            self.time = datetime.now()

    def to_dict(self) -> Dict[str, Any]:
        data = super().to_dict()
        data["time"] = self.time.isoformat()  # Ensure datetime is serialized as string
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        if "time" in data and isinstance(data["time"], str):
            data["time"] = datetime.fromisoformat(data["time"])
        return cls(
            msg=data["msg"],
            level=data.get("level", INFO),
            time=data.get("time")
        )


@dataclass
class TickEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    datetime: datetime

    name: str = ""
    volume: float = 0
    turnover: float = 0
    open_interest: float = 0
    last_price: float = 0
    last_volume: float = 0
    limit_up: float = 0
    limit_down: float = 0

    open_price: float = 0
    high_price: float = 0
    low_price: float = 0
    pre_close: float = 0

    pct_chng: float = 0 #当前tick的成交价（last_price）相比pre_close的百分比涨跌幅
    net_chng: float = 0  # 当前tick的成交价（last_price）相比pre_close的差值涨跌幅

    bid_price_1: float = 0
    bid_price_2: float = 0
    bid_price_3: float = 0
    bid_price_4: float = 0
    bid_price_5: float = 0

    ask_price_1: float = 0
    ask_price_2: float = 0
    ask_price_3: float = 0
    ask_price_4: float = 0
    ask_price_5: float = 0

    bid_volume_1: float = 0
    bid_volume_2: float = 0
    bid_volume_3: float = 0
    bid_volume_4: float = 0
    bid_volume_5: float = 0

    ask_volume_1: float = 0
    ask_volume_2: float = 0
    ask_volume_3: float = 0
    ask_volume_4: float = 0
    ask_volume_5: float = 0

    localtime: datetime = None  # This field is explicitly included in the dataclass

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"

    def to_dict(self) -> Dict[str, Any]:
        data = super().to_dict()
        # Remove dynamically generated fields that are not part of __init__
        data.pop("ctaf_symbol", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        # Convert enum value back to Enum type
        data["exchange"] = ExchangeEnum(data["exchange"])
        # Convert datetime strings back to datetime objects
        data["datetime"] = datetime.fromisoformat(data["datetime"])
        if "localtime" in data and data["localtime"] is not None:
            data["localtime"] = datetime.fromisoformat(data["localtime"])

        # __post_init__ will correctly set ctaf_symbol
        return cls(**data)


@dataclass
class BarEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    datetime: datetime

    interval: IntervalEnum = None
    volume: float = 0
    turnover: float = 0
    open_interest: float = 0
    open_price: float = 0
    high_price: float = 0
    low_price: float = 0
    close_price: float = 0 #当前interval的最后一口价格

    pre_close: float = 0 #上一个interval的close_price

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"

    def to_dict(self) -> Dict[str, Any]:
        data = super().to_dict()
        data.pop("ctaf_symbol", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["datetime"] = datetime.fromisoformat(data["datetime"])
        if "interval" in data and data["interval"] is not None:
            data["interval"] = IntervalEnum(data["interval"])
        return cls(**data)


@dataclass
class InstructionEventData(BaseEventData):
    symbol: str
    exchange: ExchangeEnum
    direction: DirectionEnum
    type: OrderTypeEnum
    volume: float
    price: float = 0
    offset: OffsetEnum = OffsetEnum.NONE
    reference: str = ""

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        data["direction"] = self.direction.value
        data["type"] = self.type.value
        data["offset"] = self.offset.value
        data.pop("ctaf_symbol", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["direction"] = DirectionEnum(data["direction"])
        data["type"] = OrderTypeEnum(data["type"])
        data["offset"] = OffsetEnum(data["offset"])
        return cls(**data)


@dataclass
class OrderEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    orderid: str

    type: OrderTypeEnum = OrderTypeEnum.LIMIT
    direction: DirectionEnum = None
    offset: OffsetEnum = OffsetEnum.NONE
    price: float = 0
    volume: float = 0
    traded: float = 0
    status: OrderStatusEnum = OrderStatusEnum.SUBMITTING
    datetime: datetime = None
    reference: str = ""

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"
        self.ctaf_orderid: str = f"{self.gateway_name}.{self.orderid}"

    def is_active(self) -> bool:
        return self.status.value.is_active()

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        data["type"] = self.type.value
        if self.direction:
            data["direction"] = self.direction.value
        data["offset"] = self.offset.value
        data["status"] = self.status.value
        if self.datetime:
            data["datetime"] = self.datetime.isoformat()
        data.pop("ctaf_symbol", None)
        data.pop("ctaf_orderid", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["type"] = OrderTypeEnum(data["type"])
        if "direction" in data and data["direction"] is not None:
            data["direction"] = DirectionEnum(data["direction"])
        data["offset"] = OffsetEnum(data["offset"])
        data["status"] = OrderStatusEnum(data["status"])
        if "datetime" in data and data["datetime"] is not None:
            data["datetime"] = datetime.fromisoformat(data["datetime"])
        return cls(**data)


@dataclass
class TradeEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    orderid: str
    tradeid: str
    direction: DirectionEnum = None

    offset: OffsetEnum = OffsetEnum.NONE
    price: float = 0
    volume: float = 0
    datetime: datetime = None

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"
        self.ctaf_orderid: str = f"{self.gateway_name}.{self.orderid}"
        self.ctaf_tradeid: str = f"{self.gateway_name}.{self.tradeid}"

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        if self.direction:
            data["direction"] = self.direction.value
        data["offset"] = self.offset.value
        if self.datetime:
            data["datetime"] = self.datetime.isoformat()
        data.pop("ctaf_symbol", None)
        data.pop("ctaf_orderid", None)
        data.pop("ctaf_tradeid", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        if "direction" in data and data["direction"] is not None:
            data["direction"] = DirectionEnum(data["direction"])
        data["offset"] = OffsetEnum(data["offset"])
        if "datetime" in data and data["datetime"] is not None:
            data["datetime"] = datetime.fromisoformat(data["datetime"])
        return cls(**data)


@dataclass
class PositionEventData(BaseEventData):
    gateway_name: str

    symbol: str
    exchange: ExchangeEnum
    direction: DirectionEnum

    volume: float = 0
    frozen: float = 0
    price: float = 0
    pnl: float = 0
    yd_volume: float = 0

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"
        self.ctaf_positionid: str = f"{self.gateway_name}.{self.ctaf_symbol}.{self.direction.value}"

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        data["direction"] = self.direction.value
        data.pop("ctaf_symbol", None)
        data.pop("ctaf_positionid", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["direction"] = DirectionEnum(data["direction"])
        return cls(**data)


@dataclass
class AccountEventData(BaseEventData):
    gateway_name: str

    accountid: str

    balance: float = 0
    frozen: float = 0

    def __post_init__(self) -> None:
        self.available: float = self.balance - self.frozen
        self.ctaf_accountid: str = f"{self.gateway_name}.{self.accountid}"

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        # 'available' and 'ctaf_accountid' are calculated in __post_init__, so don't include
        data.pop("available", None)
        data.pop("ctaf_accountid", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        return cls(**data)


@dataclass
class QuoteEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    quoteid: str

    bid_price: float = 0.0
    bid_volume: int = 0
    ask_price: float = 0.0
    ask_volume: int = 0
    bid_offset: OffsetEnum = OffsetEnum.NONE
    ask_offset: OffsetEnum = OffsetEnum.NONE
    status: OrderStatusEnum = OrderStatusEnum.SUBMITTING
    datetime: datetime = None
    reference: str = ""

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"
        self.ctaf_quoteid: str = f"{self.gateway_name}.{self.quoteid}"

    def is_active(self) -> bool:
        return self.status.value.is_active()

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        data["bid_offset"] = self.bid_offset.value
        data["ask_offset"] = self.ask_offset.value
        data["status"] = self.status.value
        if self.datetime:
            data["datetime"] = self.datetime.isoformat()
        data.pop("ctaf_symbol", None)
        data.pop("ctaf_quoteid", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["bid_offset"] = OffsetEnum(data["bid_offset"])
        data["ask_offset"] = OffsetEnum(data["ask_offset"])
        data["status"] = OrderStatusEnum(data["status"])
        if "datetime" in data and data["datetime"] is not None:
            data["datetime"] = datetime.fromisoformat(data["datetime"])
        return cls(**data)


@dataclass
class ContractEventData(BaseEventData):
    gateway_name: str
    symbol: str
    exchange: ExchangeEnum
    name: str
    product: ProductEnum
    size: float
    pricetick: float

    min_volume: float = 1
    stop_supported: bool = False
    net_position: bool = False
    history_data: bool = False

    option_strike: float = 0
    option_underlying: str = ""
    option_type: OptionTypeEnum = None
    option_listed: datetime = None
    option_expiry: datetime = None
    option_portfolio: str = ""
    option_index: str = ""

    def __post_init__(self) -> None:
        self.ctaf_symbol: str = f"{self.symbol}.{self.exchange.value}"

    def to_dict(self) -> Dict[str, Any]:
        data = asdict(self)
        data["exchange"] = self.exchange.value
        data["product"] = self.product.value
        if self.option_type:
            data["option_type"] = self.option_type.value
        if self.option_listed:
            data["option_listed"] = self.option_listed.isoformat()
        if self.option_expiry:
            data["option_expiry"] = self.option_expiry.isoformat()
        data.pop("ctaf_symbol", None)
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        data["exchange"] = ExchangeEnum(data["exchange"])
        data["product"] = ProductEnum(data["product"])
        if "option_type" in data and data["option_type"] is not None:
            data["option_type"] = OptionTypeEnum(data["option_type"])
        if "option_listed" in data and data["option_listed"] is not None:
            data["option_listed"] = datetime.fromisoformat(data["option_listed"])
        if "option_expiry" in data and data["option_expiry"] is not None:
            data["option_expiry"] = datetime.fromisoformat(data["option_expiry"])
        return cls(**data)

# --- 事件数据反序列化工厂函数 ---
# 这是一个辅助函数，用于根据 event_type 从字典中重建正确的 BaseEventData 子类实例。
def reconstruct_event_data_from_dict(event_type: EventTypeEnum, data: Dict[str, Any]) -> BaseEventData:
    """
    Factory function to reconstruct BaseEventData subclasses from a dictionary.
    This needs to be exhaustive for all possible EventData types you might send.
    """
    if event_type == EventTypeEnum.EVENT_LOG:
        return LogEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_TICK:
        return TickEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_INSTRUCTION:
        return InstructionEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_ORDER:
        return OrderEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_TRADE:
        return TradeEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_POSITION:
        return PositionEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_ACCOUNT:
        return AccountEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_QUOTE:
        return QuoteEventData.from_dict(data)
    elif event_type == EventTypeEnum.EVENT_CONTRACT:
        return ContractEventData.from_dict(data)
    # 添加其他 EventTypeEnum 到对应 EventData 类的映射
    else:
        # 如果没有匹配的特定类型，尝试作为通用的 BaseEventData 处理
        return BaseEventData.from_dict(data)